/*
 ============================================================================
 Name        : orgaTp1.c
 Author      : 
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "math.h"
#include "time.h"

#define C_WORD_BUFFER_SIZE 100
#define WORD_BUFFER_BLOCK 20000
#define PARAM_ID	'-'
#define ENDL	'\n'
#define CR	'\r'

enum ejec {HELP, VERSION, BUBBLE, SHELL, ERROR};


//	Devuelve las posiciones de los parametros en params y las posiciones
//	de los argumentos en args. Parametros = contienen -
//	Argumentos los que no tienen -
void split_params(char** argv, unsigned int cant, unsigned int* params, unsigned int* args) {
	unsigned int nparam = 0;
	unsigned int narg = 0;
	for(int i = 1; i < cant; ++i) {
		if(*(argv[i]) == PARAM_ID)
			params[nparam++] = i;
		else
			args[narg++] = i;
	}
	params[nparam] = 0;
	args[narg] = 0;
}

void trim(char* str, unsigned int size) {
	char* aux;
	aux = (char*)malloc((size + 1)* sizeof(char));
	unsigned int i;
	for(i = 0; i < size; ++i) {
		if((str[i] != ' ') && (str[i] != '\r') && (str[i] != '\n'))
			aux[i] = str[i];
	}
	aux[i] = 0;
	strcpy(str, aux);
	free(aux);
}

void swap(char* x, char* y) {
	char aux;
	aux = *y;
	*y = *x;
	*x = aux;
}

void swapStrings(char** x, char** y) {
	char* aux;
	aux = *y;
	*y = *x;
	*x = aux;
}

void shellSort(char** array, unsigned int size) {
	unsigned int total_steps = log2(size);
	unsigned int gap;
	char* temp;
	for(unsigned int i = 1; i <= total_steps; ++i) {
		gap = size / exp2(i);
		for(unsigned int j = gap; j < size; ++j) {
			temp = array[j];
			unsigned int k;
			for(k = j; (k >= gap) && (strcmp(array[k - gap], temp) > 0); k -= gap) {
				array[k] = array[k - gap];
			}
			array[k] = temp;
		}
	}
}

void bubbleSort(char** array, unsigned int size) {
	unsigned int i = 0;
	unsigned int j = 0;
	for(j = 0; j < size; j++) {
		for(i = 0; i < size - (j + 1); i++) {
			if(strcmp(array[i], array[i + 1]) > 0)
				swapStrings(&(array[i]), &(array[i + 1]));
		}
	}
}

//	devuelve longitud del vector vec
unsigned int veclen(const unsigned int* vec) {
	unsigned int i = 0;
	while(vec[i] != 0)
		++i;
	return i;
}

//	Hipotesis: si hay varios parametros, se utiliza el primero de ellos
unsigned int select_mode(unsigned int* posparams, char** params) {
	unsigned int cant = veclen(posparams);
	if(!cant)
		return BUBBLE;	//  por defecto utiliza bubblesort
	for(unsigned int i = 0; i < cant; ++i) {
		if(!strcmp(params[posparams[i]], "-h"))
			return HELP;
		if(!strcmp(params[posparams[i]], "-V"))
			return VERSION;
		if(!strcmp(params[posparams[i]], "-b"))
			return BUBBLE;
		if(!strcmp(params[posparams[i]], "-s"))
			return SHELL;
	}
	return ERROR;	//  si no es uno de los modos anteriores, error
}

void showFileError() {
	printf("Error abriendo el/los archivos indicados");
}

void showHelp() {
	printf("tp0 [OPTIONS][file...]\n"
			"-h, --help			display this help and exit.\n"
			"-V, --version		display version information and exit\n"
			"-b, --bubble		use the bubblesort algorithm\n"
			"-s, --shell		use the shellsort algorithm\n");
}

void showVersion() {
	printf("tp0 version 1.0");
}

void showParametersError() {
	printf("parametros incorrectos, utilice -h para informacion sobre la forma de uso");
}


int executeSort(unsigned char mode, unsigned int size, char** const words) {
	time_t beg, end;
	switch(mode) {
	case SHELL: {
		beg = clock();
		shellSort(words, size);
		end = clock() - beg;
		break;
	}
	case BUBBLE: {
		beg = clock();
		bubbleSort(words, size);
		end = clock() - beg;
		break;
	}
	default:
		return -1;
	}
	printf("Tiempo de procesamiento: %d", (int)end);
	return 0;
}

int executeMode(unsigned char mode) {
	switch(mode) {
	case HELP: {
		showHelp();
		return 0;
	}
	case VERSION: {
		showVersion();
		return 0;
	}
	case ERROR: {
		showParametersError();
		return 1;
	}
	default:
		return 0;
	}
}

int main(int argc, char** argv) {
	unsigned int *params, *args;
	params = malloc((argc - 1) * sizeof(int));
	args = malloc((argc - 1) * sizeof(int));
	split_params(argv, argc, params, args);
	unsigned char mode = select_mode(params, argv);
	if(executeMode(mode))
		return 0;
	if(veclen(args) == 0) {
		showParametersError();
		return 0;
	}
	FILE* file;
	unsigned int word_blocks_cant = 1;
	char** words = (char**)malloc(word_blocks_cant * WORD_BUFFER_BLOCK * sizeof(char*));
	unsigned int j = 0;		//  current word
	unsigned int i = 0;		//	current character
	unsigned int filen = 0;	//	number of files
	for(unsigned int k = 0; k < veclen(args); ++k) {
		file = fopen(argv[args[filen]], "r");
		if(!ferror(file)) {
		while(!feof(file)) {
			char c = fgetc(file);
			char* buffer = (char*)malloc(C_WORD_BUFFER_SIZE);
			i = 0;		//	comienza nueva palabra
			while(c != ' ' && c != ENDL && c != CR && c != EOF && c != 0 && !feof(file)) {
				buffer[i] = c;
				c = fgetc(file);
				++i;
			}
			if(i) {		//	si se leyo al menos 1 caracter
				words[j] = buffer;
				++j;
			}
			//	en caso de que se requiera mas memoria
			if((j / (word_blocks_cant * WORD_BUFFER_BLOCK)) >= 1) {
				word_blocks_cant++;
				words = (char**)realloc(words,(word_blocks_cant) * WORD_BUFFER_BLOCK * sizeof(char*));
			}
		}
		fclose(file);
		++filen;
		} else {
			showFileError();
			return -1;
		}
	}
	executeSort(mode, j, words);
	FILE* fout = fopen("salida.txt", "w");
	for(int i = 0; i < j; ++i) {
		fprintf(fout, "%s", words[i]);
		fputc(' ',fout);
	}
	free(params);
	free(args);
	for(int i = 0; i < j; ++i) {
		free(words[i]);
	}
	free(words);
	return EXIT_SUCCESS;
}

